package com.xfcy.media.service.jobhandler;

import com.xfcy.base.utils.Mp4VideoUtil;
import com.xfcy.media.mapper.MediaProcessMapper;
import com.xfcy.media.model.po.MediaProcess;
import com.xfcy.media.service.MediaFileProcessService;
import com.xfcy.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 视频处理任务类
 */
@Slf4j
@Component
public class VideoTask {

    @Autowired
    MediaFileProcessService mediaFileProcessService;

    @Autowired
    MediaFileService mediaFileService;

    // ffmpegpath的安装路径
    @Value("${videoprocess.ffmpegpath}")
    private String ffmpegpath;

    /**
     * 视频处理任务
     */
    @XxlJob("videoJobHandler")
    public void videoJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        // 确定cpu的核心数
        int processors = Runtime.getRuntime().availableProcessors();

        // 获取待处理的任务
        List<MediaProcess> mediaProcessList = mediaFileProcessService.getMediaProcessList(shardIndex, shardTotal, processors);
        // 任务的数量是mediaProcessList的大小
        int size = mediaProcessList.size();
        log.debug("取到视频任务数：{}", size);
        if (size <= 0) {
            return;
        }
        // 创建一个线程池
        ExecutorService service = Executors.newFixedThreadPool(size);
        // 使用计数器
        CountDownLatch countDownLatch = new CountDownLatch(size);

        mediaProcessList.forEach(mediaProcess -> {
            // 将任务加入线程池
            service.execute(() -> {
                try {
                    // 任务执行逻辑
                    // 任务id
                    Long id = mediaProcess.getId();
                    // 开启任务
                    boolean b = mediaFileProcessService.startTask(id);
                    if (!b) {
                        log.debug("抢占任务失败，任务id:{}", id);
                        return;
                    }

                    // 桶
                    String bucket = mediaProcess.getBucket();
                    String filePath  = mediaProcess.getFilePath();
                    // 文件id md5值
                    String fileId = mediaProcess.getFileId();
                    // 下载minio视频到本地
                    File file = mediaFileService.downloadFileFromMinIO(bucket, filePath );
                    if (file == null) {
                        log.debug("下载视频出错，任务id:{}, bucket:{}, objectName:{}", id, bucket, filePath);
                        // 保存任务为失败
                        mediaFileProcessService.saveProcessFinishStatus(id, "3", fileId, null, "下载视频出错");
                        return;
                    }

                    // 原avi视频文件的路径
                    String video_path = file.getAbsolutePath();
                    // 转换后mp4文件的名称
                    String mp4_name = fileId + ".mp4";
                    // 转换后mp4文件路径
                    // 先创建一个临时文件，作为转换后的文件
                    File mp4File = null;
                    try {
                        mp4File = File.createTempFile("minio", ".mp4");
                    } catch (IOException e) {
                        log.debug("创建临时文件异常，{}", e.getMessage());
                        // 保存任务为失败
                        mediaFileProcessService.saveProcessFinishStatus(id, "3", fileId, null, "创建临时文件异常");
                        return;
                    }
                    String mp4_path = mp4File.getAbsolutePath();
                    // 创建工具类对象
                    Mp4VideoUtil videoUtil = new Mp4VideoUtil(ffmpegpath, video_path, mp4_name, mp4_path);
                    // 开始视频转换，成功返回success，失败返回失败原因
                    String result = videoUtil.generateMp4();
                    if (!result.equals("success")) {

                        log.debug("视频转码失败，bucket:{}, objectName:{}, 原因：{}", bucket, filePath , result);
                        mediaFileProcessService.saveProcessFinishStatus(id, "3", fileId, null, result);
                        return;
                    }
                    // mp4文件的url
                    String objectName  = getFilePathByMd5(fileId, ".mp4");
                    //访问url
                    String url = "/" + bucket + "/" + objectName;
                    // 上传到minio
                    boolean b1 = mediaFileService.addMediaFilesToMinIO(mp4_path, "video/mp4", bucket, objectName);
                    if (!b1) {
                        log.debug("上传mp4到minio失败，taskId:{},bucket:{}, objectName:{}, 原因：{}", id, bucket, filePath , result);
                        mediaFileProcessService.saveProcessFinishStatus(id, "3", fileId, null, "上传mp4到minio失败");
                        return;
                    }
                    // 更新任务为成功
                    mediaFileProcessService.saveProcessFinishStatus(id, "2", fileId, url, "下载视频出错");
                } finally {
                    countDownLatch.countDown();
                }

            });
        });

        // 阻塞 最多等待30分钟，如果出现countDownLatch没归零，要有保底策略
        countDownLatch.await(30, TimeUnit.MINUTES);
    }

    /**
     * 得到合并后的文件的地址
     *
     * @param fileMd5 文件id即md5值
     * @param fileExt 文件扩展名
     * @return
     */
    private String getFilePathByMd5(String fileMd5, String fileExt) {
        return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + fileExt;
    }

}
